/*
 * at_parser.c
 *
 *  Created on: 202211/7
 *      Author: yanghx
 */

#include "stdio.h"
#include "string.h"
#include "stdarg.h"
#include "stdlib.h"
#include "at_parser.h"
#include "at_list.h"
#include "comm_data_pack.h"
#include "wl_queue.h"
#include "module_config.h"
#include "amLog.h"
#include "stm32g4xx_it.h"

unsigned char au8AtTxBuffer[BUF_AT_ITEM_SIZE];

uint32_t   g_u32Port = 0;

typedef int (*pfnATHandle)( uint32_t, char* );
typedef struct _ATList_
{
    const char* ps8ATcmd;
    pfnATHandle pfnHandle;
}ATList_st;

const ATList_st g_special_cmd_list[] =
{
    {"VER?",          at_version},
};

const ATList_st g_com_cmd_list[] =
{ 
    {"+EENFAC",             enter_fac},
    {"+ELOG=",              log_ctrl},
    {"+EFWV?",              fw_ver_read},
    {"+EBWV?",              boot_ver_read},
    {"+EHWID?",             hardware_id_read},
    {"+EBAT?",              get_bat_info},
    {"+EBATPN?",            get_cell_infor},
    {"+EHALL?",             hall_read},
    // {"+ESUBGCFG?",          sub_cfg_read},
    {"+ESUBG?",             subg_read},
    {"+ESUBGPAIR=",         subgPair},
    {"+ESUBGDEID=",         subgDeletePairID},
    {"+EKEY?",              key_read},
    {"+EPINR=",             pin_read},
    {"+ENTC?",              ntc_read},
    {"+ECUR=",              current_read},
    {"+EPN?",               pn_read},
    {"+ECPUID?",            cpu_id_read},
    {"+EOATVER=",           get_other_at_version},
    {"+EPG=",               power_good_read},
    {"+EADCVOLT=",          adc_volt_read},
    {"+EVOLT=",             volt_read},
    {"+EPOWERS?",           power_on_source_read},
    {"+EDCINCTRL=",         dc_in_ctrl},
    // not release
    {"+EFDATA=",            flash_info_read},
    {"+EGPIO=",             gpio_test},

    {"+EMR=",               mem_read},
    {"+ELOGLVL?",           log_level_get},
    {"+ELOGLVL=",           log_level_set},
    {"+EMLOGLVL=",          module_log_level_set},
};

const ATList_st g_fac_cmd_list[] =
{
    {"+EEXFAC",             exit_fac},
    {"+ECHG=",              charge_ctrl},
//    {"+ECHGCUR?",           charge_cur},
//    {"+ECHGVOL?",           charge_vol},
    {"+ESUBGCFG=",          sub_cfg_write},
    {"+ESUBGENTEST",        sub_test_en},
    {"+ESUBGEXTEST",        sub_test_ext},
    {"+ESUBG=",             sub_para_config},
    {"+ESUBGTX=",           sub_test_tx},
    {"+ESUBGRET=",          sub_ret_val},
    {"+EBUZZER=",           buzzer_test},
    {"+EUPUART=",           uart_up_test},
    {"+EIMUSTATE=",         imu_state_ret},
    {"+ESUBGCOM",           sub_com_test},
    {"+EORESET=",           other_mcu_reset},
    {"+EPOWER=",            power_switch},
    {"+ESIGN?",             sign_read},
    {"+ESIGN=",             sign_write},
    {"+ERECOVER=",          recover_ctrl},
    {"+EPINW=",             pin_write},
    {"+ERESET",             dev_reset},
    {"+EPWRDOWN",           dev_powerdown},
    
    // not release
    {"+EMW=",               mem_write},
    {"+ECHGCUR=",           charge_cur_config},
    {"+ECHGVOL=",           charge_vol_config},
    {"+EIPWM=",             charge_i_pwm},
    {"+EVPWM=",             charge_v_pwm},
    {"+EPN=",               pn_write},
    {"+EI2CGPIO=",          i2cGPIOtest},
    {"+ESN=",               sn_write},
    {"+EDIS=",              display_test},
    {"+EERROR=",            errorcode_test},
    {"+ERGBPWM=",           rgb_led_test},
    {"+EEMCENTEST",        emc_test_enter},
    {"+EEMCTX=",            EMC_TX_Test},
    {"+EEMCRX=",            EMC_RX_Test},    
};

static int do_ATCmd(uint32_t u32Port, char* p8Param, const ATList_st* stATCmdList, uint8_t n)
{
    int s32Pos;
    int s32Ret = ERR_AT_INVALID_CMD;
    
    for(int i = 0; i < n; i++)
    {
        if(NULL != stATCmdList[i].ps8ATcmd)
        {
            s32Pos = strlen(stATCmdList[i].ps8ATcmd);
            if(0 == memcmp(p8Param, stATCmdList[i].ps8ATcmd, s32Pos))
            {
                if (NULL != stATCmdList[i].pfnHandle)
                {
                    stATCmdList[i].pfnHandle(u32Port, &p8Param[s32Pos]);
                    
                    return ERR_AT_NULL;
                }       
            }
        }
    }
    return s32Ret;
}

void at_rsp(uint32_t u32Port, const char* ps8Format, ...)
{
    static char s8ATRsp[BUF_LOG_ITEM_SIZE];
	int s32Size;
    va_list  ap;
    
    va_start(ap, ps8Format);
    s32Size = vsnprintf(s8ATRsp, BUF_AT_ITEM_SIZE - 1, ps8Format, ap);
    va_end(ap); 
    s8ATRsp[s32Size] = 0;
    
    if (0 == u32Port)
    {
        uart_tx_startDMA(&DEBUG_UART, (uint8_t *)s8ATRsp, s32Size); //, (s32Size/90 + 2));
    }
    else
    {
        // sendATRspToUpper((uint8_t *)s8ATRsp, s32Size + 1);
    }
}

void at_transfer(uint32_t u32Port, const char* data,uint8_t len)
{
    static uint8_t s8ATtransfer[BUF_LOG_ITEM_SIZE];
    memcpy(s8ATtransfer,data,len);
    if (0 == u32Port)
    {
        HAL_UART_Transmit_DMA(&DEBUG_UART, s8ATtransfer, len); //, (s32Size/90 + 2));
    }
    else
    {
        // sendATRspToUpper((uint8_t *)s8ATtransfer,len);
    }
}

static int32_t at_com_parser(uint32_t u32Port, char* ps8Param) 
{
    int s32Ret = ERR_AT_INVALID_CMD;
    s32Ret = do_ATCmd(u32Port, ps8Param, g_com_cmd_list, sizeof(g_com_cmd_list)/sizeof(ATList_st));
    return s32Ret;
}

static int at_fac_parser(uint32_t u32Port, char* ps8Param)
{
    int s32Ret = ERR_AT_INVALID_CMD;
    s32Ret = do_ATCmd(u32Port, ps8Param, g_fac_cmd_list, sizeof(g_fac_cmd_list)/sizeof(ATList_st));
    return s32Ret;
}

static int at_specail_parser(uint32_t u32Port, char* ps8Param)
{
    int s32Ret = ERR_AT_INVALID_CMD;
    s32Ret = do_ATCmd(u32Port, ps8Param, g_special_cmd_list, sizeof(g_special_cmd_list)/sizeof(ATList_st));
    return s32Ret;
}

static int at_parser(uint16_t u16Port, uint16_t u16Len, char* ps8Buf)
{
    int ret = at_specail_parser(u16Port, &ps8Buf[2]);
    // char buf[BUF_AT_ITEM_SIZE] = {0};


    if(ret != ERR_AT_INVALID_CMD)
    {
        return ret;
    }
    
    if('+' != ps8Buf[2])
    {
        at_rsp(u16Port, "ERROR,%d%%\r\n", ERR_AT_INVALID_CMD);
        return ERR_AT_INVALID_CMD;

    }
    
    switch(ps8Buf[3])
    {
        case 'E':
            ps8Buf[u16Len - 1] = 0;
            ret =  at_com_parser(u16Port, &ps8Buf[2]);
            if(ERR_AT_INVALID_CMD != ret)
            {
                break;
            }
            else if(get_fac_mode_en())
            {
                ret =  at_fac_parser(u16Port, &ps8Buf[2]);
                if(ERR_AT_INVALID_CMD == ret)
                {
                    at_rsp(u16Port, "ERROR,%d%%\r\n", ERR_AT_INVALID_CMD);
                }  
            }
            else
            {
                at_rsp(u16Port, "ERROR,%d%%\r\n", ERR_AT_INVALID_MODE);
                ret =  ERR_AT_INVALID_MODE;
            }
            break;
        default:
            LOG_TS(AM_LOG_LEVEL_INFO, "At", "not support AT+%c*** cmd\r\n",ps8Buf[3]);
            at_rsp(u16Port, "ERROR,%d%%\r\n", ERR_AT_INVALID_CMD);
            ret =  ERR_AT_INVALID_CMD;
            break;
    }
    return ret;
}

void process_at_cmd(uint16_t u16Port, uint16_t u16Len, char* ps8Buf)
{
    uint8_t start = 0;
    uint8_t end = u16Len-1;

    /* ignore head space */
    while (ps8Buf[start] == ' ')
    {
        start++;
        if (start+5 > u16Len-1)             /* at cmd have 5 char at least */
        {
            return;
        }
    }

    /* ignore tail space */
    while (ps8Buf[end] == ' ')
    {
        end--;
        if (start+5 > end)
        {
            return;
        }
    }

    if ((('A' == ps8Buf[start]) && ('T' == ps8Buf[start+1])) && (ps8Buf[end] == '%'))
    {
        at_parser(g_u32Port, end-start+1, &ps8Buf[start]);
    }
}

void at_cycle_check_rsp(void)
{
    if(au8AtTxBuffer[0] == 'O' || au8AtTxBuffer[0] == 'E')
    {
        at_rsp(g_u32Port, (char*)au8AtTxBuffer);
        memset(au8AtTxBuffer, 0, sizeof(au8AtTxBuffer));
    }
}

int32_t copy_at_cmd(uint32_t u32Port, char* cmd_buffer, uint32_t u32Len)
{
    int32_t ret = commRxRingPush(DEBUG_COMM_TYPE, (uint8_t*)cmd_buffer, u32Len);
    if (ret >= 0)
    {
        g_u32Port = u32Port;
    }

    // printf("uart ring failed:%d\r\n",ret);
    return ret;
}